﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.SceneManagement;

public class Utility
{
    public static float FramePreSecond = 60f;

    public static Vector2 CameraSize(Camera camera){
        Vector2 ret=default;
        Vector2 min=camera.ViewportToWorldPoint(new Vector3(0,0,0));
        Vector2 max=camera.ViewportToWorldPoint(new Vector3(1,1,0));
        ret=max-min;
        return ret;
    }
    public static Vector2 Transform3DPosition(Vector2 startPosition, Rect startRect, Rect targetRect)
    {
        Vector2 pixleStartPosition = startPosition * new Vector2(16f, 16f);

        Vector2 purePixleStartPosition = pixleStartPosition - new Vector2(startRect.x, startRect.y);
        Vector2 percentagePosition = purePixleStartPosition / new Vector2(startRect.width, startRect.height);
        Vector2 purePixleTargetPosition = percentagePosition * new Vector2(targetRect.width, targetRect.height);
        Vector2 pixleTargetPosition = purePixleTargetPosition + new Vector2(targetRect.x, targetRect.y);
        Vector2 targetPosition = pixleTargetPosition / new Vector2(16f, 16f);

        return targetPosition;


    }
    public static int OscillateIndex(int arrayLength, int seqIndex)
    {
        int length = arrayLength;
        int loopM = length * 2 - 2;
        int index = seqIndex % loopM;
        int oscillateIndexIndex = 0;
        if (index < length - 1)
        {
            oscillateIndexIndex = index;
        }
        else
        {
            oscillateIndexIndex = length - (index + 1 - length) - 1;
        }
        return oscillateIndexIndex;

    }
    public static bool GetObjectsWithName(string name, out IEnumerable<GameObject> gameObjects)
    {
        if (name == null) { name = ""; }
        HashSet<GameObject> ret = new();
        Stack<Transform> stack = new();
        SceneManager.GetActiveScene().GetRootGameObjects().Foreach((GameObject o) =>
        {
            stack.Push(o.transform);
        });
        while (stack.Count > 0)
        {
            Transform t = stack.Pop();
            if (name.Equals(t.name))
            {
                ret.Add(t.gameObject);
                for (int i = 0; i < t.childCount; i++)
                {
                    stack.Push(t.GetChild(i));
                }
            }
        }

        gameObjects = ret;
        return ret.Count > 0;

    }
    public static IEnumerable<GameObject> GetObjectsWithName(string name)
    {
        GetObjectsWithName(name, out IEnumerable<GameObject> ret);
        return ret;
    }
    public static GameObject GetObjectWithName(string name)
    {

        if (name == null) { name = ""; }
        GameObject ret;
        Stack<Transform> stack = new();
        ret = SceneManager.GetActiveScene().GetRootGameObjects().FindWith((GameObject o) =>
        {
            if (name.Equals(o.name))
            {
                return true;
            }
            else
            {
                stack.Push(o.transform);
                return false;
            }
        });
        if (ret == null)
        {
            while (stack.Count > 0)
            {
                Transform t = stack.Pop();
                for (int i = 0; i < t.childCount; i++)
                {
                    Transform child = t.GetChild(i);
                    if (name.Equals(child.name))
                    {
                        ret = child.gameObject;
                        break;
                    }
                    stack.Push(child);
                }
                if (ret != null)
                {
                    break;
                }
            }
        }

        return ret;
    }


    public static bool InCameraViewport(Camera camera, Vector3 position, float extendSize = 0)
    {
        bool ret = false;
        Vector2 vec_left = Camera.main.WorldToViewportPoint(position);
        ret = InViewport(vec_left.x, extendSize) && InViewport(vec_left.y, extendSize);
        return ret;
    }
    private static bool InViewport(float v, float extendSize = 0)
    {
        return v > 0f - extendSize && v < 1f + extendSize;
    }

    public static bool CheckGround(Collider2D collider, LayerMask layer, float fixOffsetY = 0.0f)
    {
        //BoxCollider2D collider = gameObject.GetComponent<BoxCollider2D>();
        //Vector2 position = collider.transform.position;
        //Vector2 offset = collider.offset;
        //Vector2 size = collider.bounds.size;
        //float y = position.y + offset.y - size.y / 2 + fixOffsetY;
        //Vector2 leftPoint = new Vector2(position.x + offset.x - size.x / 2, y);
        //Vector2 rightPoint = new Vector2(position.x + offset.x + size.x / 2, y);

        Bounds colliderBounds = collider.bounds;
        float y = colliderBounds.min.y + fixOffsetY;
        Vector2 leftPoint = new Vector2(colliderBounds.min.x, y);
        Vector2 rightPoint = new Vector2(colliderBounds.max.x, y);


        bool leftIsGround = Physics2D.Raycast(leftPoint, Vector2.down, 0.1f, layer);
        bool ret = leftIsGround;
        if (!ret)
        {
            bool rightIsGround = Physics2D.Raycast(rightPoint, Vector2.down, 0.1f, layer);
            ret = rightIsGround;
        }
        return ret;
    }



    public static bool IsLayer(GameObject gameObject, params string[] layersName)
    {
        List<string> list = new List<string>(layersName);
        return list.IndexOf(LayerMask.LayerToName(gameObject.layer)) >= 0;
    }
    public static bool IsPlayerLayer(GameObject gameObject, params string[] layersName)
    {
        return IsLayer(gameObject, "Player");
    }

    public static bool Nearest(IEnumerable<GameObject> enumerable, Vector2 point, out Vector2 nearestPoint)
    {
        return Nearest(enumerable.Transform<GameObject, Vector2>((GameObject o) =>
        {
            return o.transform.position;
        }), point, out nearestPoint);

    }
    public static bool Nearest(IEnumerable<Component> enumerable, Vector2 point, out Vector2 nearestPoint)
    {

        return Nearest(enumerable.Transform<Component, Vector2>((Component o) =>
        {
            return o.transform.position;
        }), point, out nearestPoint);
    }
    public static bool Nearest(IEnumerable<Vector2> enumerable, Vector2 point, out Vector2 nearestPoint)
    {
        bool ret = false;
        nearestPoint = Vector2.positiveInfinity;
        if (enumerable != null)
        {
            nearestPoint = enumerable.Min((Vector2 p1, Vector2 p2) =>
            {
                float distance = Vector2.Distance(point, p1) - Vector2.Distance(point, p2);
                return Mathf.Approximately(distance, 0f) ? 0 : (distance > 0f ? 1 : -1);
            });
            ret = true;
        }
        return ret;
    }


    public static Color ParseHtmlString(string colorString)
    {
        if (ColorUtility.TryParseHtmlString(colorString, out Color ret))
        {
            return ret;
        }
        else
        {
            return default(Color);
        }
    }

    public static Color CreateRGBColor(int r, int g, int b)
    {
        return new Color(r / 255f, g / 255f, b / 255f);
    }

}

/*
 * 表示八个方向
 */
public enum LookAtDirection8
{
    None = 0,
    Left,               //左
    LeftUp,             //左上
    Up,                 //上
    RightUp,            //右上
    Right,              //右
    RightDown,          //右下
    Down,               //下
    LeftDown,           //左下

}

/*
 * 表示十二个方向
 */
public enum LookAtDirection12
{
    None = 0,
    Left,               //左
    LeftLeftUp,         //左上偏左
    UpLeftUp,           //左上偏上
    Up,                 //上
    UpRightUp,          //右上偏上
    RightRightUp,       //右上偏右
    Right,              //右
    RightRightDown,     //右下偏右
    DownRightDown,      //右下偏下
    Down,               //下
    DownLeftDown,       //左下偏下
    LeftLeftDown,       //左下偏左

}

public enum EnumBodyPart
{
    Body = 0,
    Head,
    Foot,
}


[System.Serializable]
public class SeqAction
{
    public delegate void SeqUpdateAction(SeqAction seq);

    [Tooltip("动作开始前的等待时间")]
    public float beginWaitTime;
    [Tooltip("每个动作间的间隔时间")]
    public float intervalTime;
    [Tooltip("持续时间，不包括等待时间")]
    public float keepingTime;
    [System.NonSerialized]
    [Tooltip("动作序号")]
    public int seqNo;
    [System.NonSerialized]
    [Tooltip("上一个动作序号,比较卡的情况下可能先序号不连续的情况")]
    public int lastSeqNo = -1;
    [System.NonSerialized]
    [Tooltip("开始时间，开始后进入等待时间")]
    public float startTime = -1;
    [System.NonSerialized]
    [Tooltip("是否已完成")]
    public bool done;
    [Tooltip("上一次UpdateAction结束的时间")]
    private float lastActionTime = -1;

    [Tooltip("回调Action是否正在运行")]
    private bool actionRunning = false;
    [Tooltip("是否在回调Action中调用了Restart方法")]
    private bool restartWhenRunning = false;
    private bool clearWhenRunning = false;

    public readonly Dictionary<string, float> PropertyFloat = new();
    public readonly Dictionary<string, int> PropertyInteger = new();
    public readonly Dictionary<string, bool> PropertyBool = new();
    public readonly Dictionary<string, Vector3> PropertyVector = new();
    public readonly Dictionary<string, object> Property = new();

    public float DurationTime
    {
        get
        {
            if (startTime >= 0)
            {
                return Time.time - startTime - beginWaitTime;
            }
            else
            {
                return -1;
            }
        }
    }

    public float DeltaTime
    {
        get
        {
            float ret;
            if (lastActionTime > 0)
            {
                ret = Time.time - lastActionTime;
            }
            else
            {
                ret = DurationTime;
            }
            return ret;
        }
    }

    public bool IsFirstAction()
    {
        return lastSeqNo < 0;
    }

    public void ReStart()
    {
        this.ReStart(Time.time);
    }
    public void ReStart(float startTime)
    {
        if (actionRunning)
        {
            restartWhenRunning = true;
        }
        else
        {
            this.Clear();
            this.startTime = startTime;
            this.restartWhenRunning = false;
        }
    }
    public void Clear()
    {
        if (actionRunning)
        {
            clearWhenRunning = true;
        }
        else
        {
            this.startTime = -1;
            this.seqNo = -1;
            this.lastSeqNo = -1;
            this.done = false;
            this.lastActionTime = -1;
            clearWhenRunning = false;
            PropertyFloat.Clear();
            PropertyInteger.Clear();
            PropertyBool.Clear();
            PropertyVector.Clear();
            Property.Clear();
        }
    }

    public void Update(SeqUpdateAction action = null, SeqUpdateAction keepTimeOutAction = null)
    {
        if (!this.done)
        {
            if (startTime < 0)
            {
                ReStart();
            }
            if (DurationTime >= 0)
            {
                if (this.intervalTime == 0.0f)
                {
                    this.seqNo = this.lastSeqNo + 1;
                }
                else
                {
                    this.seqNo = Mathf.FloorToInt(DurationTime / this.intervalTime) + 1;
                }
                if (this.lastSeqNo != this.seqNo)
                {
                    actionRunning = true;
                    if (action != null)
                    {
                        action(this);
                    }
                    lastActionTime = Time.time;
                    this.lastSeqNo = this.seqNo;
                    actionRunning = false;
                    if (clearWhenRunning)
                    {
                        Clear();
                    }
                    if (restartWhenRunning)
                    {
                        ReStart();
                    }
                }
            }
            if (this.keepingTime > 0 && DurationTime >= this.keepingTime)
            {
                this.done = true;
                if (keepTimeOutAction != null)
                {
                    actionRunning = true;
                    keepTimeOutAction.Invoke(this);
                    lastActionTime = Time.time;
                    this.lastSeqNo = this.seqNo;
                    actionRunning = false;
                    if (restartWhenRunning)
                    {
                        ReStart();
                    }
                }
            }

        }

    }

}




